'use strict';

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// 进程状态
var UNARRIVED = 0; // 未到达
var WAIT = 1; // 就绪
var RUNNING = 2; // 运行
var FINISHED = 3; // 完成

var pcbList = []; // 所有进程的引用
var unarrivedPcbList = []; // 所有未到达的进程列表
var waitPcbList = []; // 就绪进程队列
var runningPcbList = []; // 正则运行的进程，只有一个
var finishPcbList = []; // 完成进程队列
var runTime = 0; // 距离第一个进程开始运行的时间
var timeControl = null; // 时间定时器的开关
var copyInitPcbList = []; // 深复制初始化所有的未到达进程队列

var $unarrivedTable = $('#unarrived-pcb-table');
var $waitTable = $('#wait-pcb-table');
var $runningTable = $('#running-pcb-table');
var $finishTable = $('#finished-pcb-table');
var $runTime = $('#runtime'); // 运行时间
var $timeSlice = $('#timeSlice'); // 时间片
var $avgTurnoverTime = $('#avgTurnoverTime'); // 平均周转时间


var PCB = function PCB(name) {
    _classCallCheck(this, PCB);

    this.name = name; // 进程名
    this.prio = 1; // 优先数（响应比）
    this.arrivaltime = ranNum(0, 5); // 等待到达就绪队列的时间，单位秒，下同
    this.needtime = ranNum(1, 10); // 需要运行的时间
    this.wasttime = 0; // 本进程已运行的时间
    this.waittime = 0; // 本进程已等待的时间
    this.state = UNARRIVED; // 进程状态 0. UNARRIVED 1.WAIT 2.RUNNING 3.FINISHED
    this.finishtime = -1;
    this.updatePrio = function () {
        // 更新优先级，保留两位小数
        this.prio = ((this.waittime + this.needtime) / this.needtime).toFixed(2);
    };
};

// 随机生成a~b的整数


function ranNum(a, b) {
    return Math.round(Math.random() * (b - a) + a);
}

// 初始化n个进程
function initPCB(n) {
    PCB.pcbsum = n;
    if (copyInitPcbList.length > 0) {
        unarrivedPcbList = [];
        for (var i = 0; i < n; i++) {
            unarrivedPcbList[i] = {};
            jQuery.extend(true, unarrivedPcbList[i], copyInitPcbList[i]);
        }
    } else {
        for (var _i = 0; _i < n; _i++) {
            unarrivedPcbList[_i] = new PCB('进程' + (_i + 1));
        }
        unarrivedPcbList[0].arrivaltime = 0; // 第一个进程到达时间从0开始

        for (var _i2 = 0; _i2 < n; _i2++) {
            pcbList[_i2] = unarrivedPcbList[_i2]; // 保留所有进程的引用
        }

        // 将unarrivedPcbList深拷贝到copyInitPcbList数组中
        for (var _i3 = 0; _i3 < n; _i3++) {
            copyInitPcbList[_i3] = {};
            jQuery.extend(true, copyInitPcbList[_i3], unarrivedPcbList[_i3]);
        }
    }

    pcbList = [];
    waitPcbList = [];
    runningPcbList = [];
    finishPcbList = [];
    runTime = 0;

    clearInterval(timeControl);
    $timeSlice.text('');
    $avgTurnoverTime.text('');
}

// 打印进程函数
function printPCB() {
    var str = '';
    $runTime.text(runTime);

    str = getPcbHtml(unarrivedPcbList);
    $unarrivedTable.html(str);

    str = getPcbHtml(runningPcbList);
    $runningTable.html(str);

    str = getPcbHtml(waitPcbList);
    $waitTable.html(str);

    str = getPcbHtml(finishPcbList);
    $finishTable.html(str);
}

// 计算平均周转时间
function getAvgTurnoverTime() {
    var allTime = 0;
    var i = 0;
    var len = 0;
    for (i = 0, len = finishPcbList.length; i < len; i++) {
        allTime += finishPcbList[i].finishtime;
    }
    return allTime / len;
}

// 查看有没有到达的队列，有则加入就绪中
function checkArrivedPcb() {
    for (var i = unarrivedPcbList.length - 1; i >= 0; i--) {
        var pb = unarrivedPcbList[i];
        if (pb.arrivaltime === runTime) {
            // 加入就绪队列
            unarrivedPcbList.splice(i, 1); // 从进程队列中取到达时间达到的进程插入就绪队列
            pb.state = WAIT;
            waitPcbList.push(pb);
        }
    }
}

// 进程运行结束时，将运行进程放入完成队列
function addRunToFinish() {
    var pb = runningPcbList.shift();
    pb.state = FINISHED;
    pb.finishtime = runTime - pb.arrivaltime;
    finishPcbList.push(pb);
}

// 如果传入参数，则按sortBy将就绪队列排序后，再取出对头进行执行
// 如果没传入桉树，直接取出对头进行执行
function evalWaitPcb(sortBy) {
    if (waitPcbList.length > 0) {
        if (!!sortBy) {
            // 传入了参数，需要排序
            // 按执行需要的时间升序将就绪进程排列
            waitPcbList.sort(function (p1, p2) {
                return p1[sortBy] - p2[sortBy];
            });
        }

        // 取出优先级最高的运行
        var pb = waitPcbList.shift(); // 从就绪队列中取最短的进程进行执行
        pb.state = RUNNING;
        runningPcbList.push(pb);
    }
}

// 短进程优先算法
// 思路：
// 1、初始化
// 2、过段时间后，修改就绪进程中的相关信息
// 3、查看有没有新进程到达，有则加入就绪队列
// 4、看有没有进程在运行，没有则从就绪队列中取出执行，
//     有则修改运行进程中的相关信息，再判断运行进程是否结束，结束则从就绪队列中取出执行
// 5、进程没有执行完，则重复2、3、4、5

function SJF() {
    // 初始化，0s时刻开始
    initSJF();
    printPCB();

    var pb = null;
    timeControl = setInterval(function () {
        runTime++;

        // 更新就绪队列中所有进程的等待时间和响应比（优先权）
        waitPcbList.forEach(function (pcb) {
            pcb.waittime += 1;
            pcb.updatePrio();
        });

        // 查看有没有到达的队列
        checkArrivedPcb();

        // 没进程在运行
        if (runningPcbList.length === 0) {
            // 没进程在运行
            evalWaitPcb('needtime');
        } else {
            // 有进程在运行，更新运行进程中的信息
            runningPcbList[0].wasttime += 1;

            if (runningPcbList[0].needtime === runningPcbList[0].wasttime) {
                // 运行结束

                // 将运行进程加入完成队列
                addRunToFinish();

                // 取出并执行就绪队列中的最短进程
                evalWaitPcb('needtime');
            }
        }
        printPCB();

        if (finishPcbList.length === PCB.pcbsum) {
            $avgTurnoverTime.text(getAvgTurnoverTime());
            clearInterval(timeControl);
        }
    }, 1000);
}

function initSJF() {
    checkArrivedPcb();
    if (runningPcbList.length === 0) {
        // 没进程在运行
        evalWaitPcb('needtime');
    }
}

// 时间片轮转算法
function RR() {
    initRR();
    printPCB();

    var pb = null;
    var timeSlice = ranNum(3, 6); // 时间片
    $timeSlice.text(timeSlice);
    var countTime = 0; // 用于计算时间片是否到了
    timeControl = setInterval(function () {
        runTime++;
        countTime++;

        // 更新就绪队列中所有进程的等待时间和响应比（优先权）
        waitPcbList.forEach(function (pcb) {
            pcb.waittime += 1;
            pcb.updatePrio();
        });

        // 查看有没有到达的队列
        checkArrivedPcb();

        // 没进程在运行
        if (runningPcbList.length === 0) {
            // 没进程在运行
            evalWaitPcb();
        } else {
            // 有进程在运行，更新运行进程中的信息
            runningPcbList[0].wasttime += 1;

            if (runningPcbList[0].needtime === runningPcbList[0].wasttime) {
                // 运行结束

                // 将运行进程加入完成队列
                addRunToFinish();

                countTime = 0;

                // 取出并执行就绪队列中的进程
                evalWaitPcb();
            } else if (countTime === timeSlice) {
                // 时间片到了
                pb = runningPcbList.shift();
                pb.state = WAIT;
                pb.waittime = 0;
                waitPcbList.push(pb); // 加入到就绪队列中的尾部

                evalWaitPcb();
                countTime = 0;
            }
        }
        printPCB();

        if (finishPcbList.length === PCB.pcbsum) {
            $avgTurnoverTime.text(getAvgTurnoverTime());
            clearInterval(timeControl);
        }
    }, 1000);
}

function initRR() {
    checkArrivedPcb();
    if (runningPcbList.length === 0) {
        // 没进程在运行
        evalWaitPcb();
    }
}

// 高响应比优先算法
function HRRN() {
    initHRRN();
    printPCB();

    var pb = null;
    timeControl = setInterval(function () {
        runTime++;

        // 更新就绪队列中所有进程的等待时间和响应比（优先权）
        waitPcbList.forEach(function (pcb) {
            pcb.waittime += 1;
            pcb.updatePrio();
        });

        // 查看有没有到达的队列
        checkArrivedPcb();

        // 没进程在运行
        if (runningPcbList.length === 0) {
            // 没进程在运行
            evalWaitPcb('prio');
        } else {
            // 有进程在运行，更新运行进程中的信息
            runningPcbList[0].wasttime += 1;

            if (runningPcbList[0].needtime === runningPcbList[0].wasttime) {
                // 运行结束

                // 将运行进程加入完成队列
                addRunToFinish();

                // 取出并执行就绪队列中的最短进程
                evalWaitPcb('prio');
            }
        }
        printPCB();

        if (finishPcbList.length === PCB.pcbsum) {
            $avgTurnoverTime.text(getAvgTurnoverTime());
            clearInterval(timeControl);
        }
    }, 1000);
}

function initHRRN() {
    checkArrivedPcb();
    if (runningPcbList.length === 0) {
        // 没进程在运行
        evalWaitPcb('prio');
    }
}

// 端进程优先入口函数
function mainSJF() {
    initPCB(5);
    SJF();
}

// 时间片轮转入口函数
function mainRR() {
    initPCB(5);
    RR();
}

// 优先级算法入口函数
function mainHRRN() {
    initPCB(5);
    HRRN();
}

// 页面相关操作
var funList = [mainSJF, mainRR, mainHRRN];
$('.btn').each(function (i, btn) {
    var $this = $(this);
    $this.on('click', function () {
        funList[i]();
        $this.addClass('active').siblings().removeClass('active');
    });
});

$('.sjf').click();

// 生成html相关模板
function getPcbHtml(pcblist) {
    var str = '<tr> <th>进程名</th> <th>prio</th> <th>arrivaltime</th> <th>needtime</th> <th>wasttime</th> <th>waittime</th><th>finishtime</th> </tr>';
    if (pcblist.length > 0) {
        //  // 在条件判断与条件内的循环间可能插入新的进程！
        for (var i = 0; i < pcblist.length; i++) {
            str += '<tr><td>' + pcblist[i].name + '</td>' + '<td>' + pcblist[i].prio + '</td>' + '<td>' + pcblist[i].arrivaltime + '</td>' + '<td>' + pcblist[i].needtime + '</td>' + '<td>' + pcblist[i].wasttime + '</td>' + '<td>' + pcblist[i].waittime + '</td>' + '<td>' + pcblist[i].finishtime + '</td>' + '</tr>';
        }
    } else {
        str = '<tr><td>空</td></tr>';
    }
    return str;
}